package com.inspectortime.service.remind;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Timer;
import java.util.TimerTask;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hibernate.Criteria;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.criterion.Restrictions;
import org.springframework.util.StringUtils;

import com.inspectortime.domain.Inspection;
import com.inspectortime.domain.impl.InspectionImpl;
import com.inspectortime.domain.type.InspectionStatus;
import com.inspectortime.mail.service.AbstractMailMessage;
import com.inspectortime.mail.service.EmailDeliveryException;
import com.inspectortime.mail.service.MailService;
import com.inspectortime.mail.service.StaticMailMessage;
import com.inspectortime.mail.service.StaticMailMessageTemplateNames;

/**
 * This agent runs on a scheduled basis and sends e-mail reminders to inspectors about home
 * buyers who quick-booked an inspection but didn't finish all the required info
 * 
 * @author mkvalsvik
 * 
 */
public class ReminderAfterQuickBookAgent extends TimerTask {

	protected static Log log = LogFactory
			.getLog(ReminderAfterQuickBookAgent.class);

	private static final String TEMP_INSPECTION_COMPLETION_CONTROLLER = "schedule2-rt.html";
	private int delayBeforeSendingReminderInMins = 30;
	private MailService mailService;

	private SessionFactory sessionFactory;

	public ReminderAfterQuickBookAgent(int initialDelayInMins, int delayBetweenRunsInMins, int delayBeforeSendingReminderInMins) {
		Timer t = new Timer("QuickBookReminder", true);
		t.schedule(this, initialDelayInMins * 60 * 1000, delayBetweenRunsInMins * 60 * 1000);
		this.delayBeforeSendingReminderInMins = delayBeforeSendingReminderInMins;
	}

	private String createInspectionLink(Inspection inspection) throws InspectionProcessingException {

		String requestedUrl = inspection.getRequestUrl();
		if (!StringUtils.hasText(requestedUrl)) {
			// Must abort, we can't send email
			throw new InspectionProcessingException("Missing inspection.requestUrl");
		}
		String inspectionLinkBaseUrl = requestedUrl.substring(0, requestedUrl
				.lastIndexOf("/"));

		return inspectionLinkBaseUrl + "/"
				+ TEMP_INSPECTION_COMPLETION_CONTROLLER + "?id="
				+ inspection.getId();
	}

	@Override
	public void run() {

		// Note: We create a session separately in order to avoid confusion with
		// the sessions that are bound to tomcat handler threads
		Session session = sessionFactory.openSession();

		// Find all quickbooked inspections for which no alert has been sent
		Criteria crit = session.createCriteria(Inspection.class);
		crit.add(Restrictions.eq("inspectionStatus",
				InspectionStatus.QUICK_BOOK));
		crit.add(Restrictions.eq("quickBookReminderHasBeenSent", false));
		List<InspectionImpl> inspections = crit.list();
		int count = 0;

		long reminderCutoffTime = System.currentTimeMillis()
				- (this.delayBeforeSendingReminderInMins * 60 * 1000);
		for (InspectionImpl inspection : inspections) {

			// Check last modified date to see if enough time has elapsed to
			// send an email
			if (inspection.getLastModifiedTime().getTime() < reminderCutoffTime) {

				// ... and send an email with a link to allow for
				// completion of the inspection at a later time
				try {
					this.sendInspectorEmail(inspection);
					inspection.setQuickBookReminderHasBeenSent(true);
					// DONT'T work because it will associate a thread with the
					// current running thread: inspection.save();
					session.update(inspection);
					count++;
				} catch (EmailDeliveryException e) {

					// Simply log the error. The fact that we're not saving the
					// ReminderHasBeenSent flag unless we don't get an exception
					// will ensure that a resend attempt is made later
					log.warn("Could not deliver e-mail, will re-try next time agent runs", e);
				} catch (InspectionProcessingException e) {

					// Simply log the error. The fact that we're not saving the
					// ReminderHasBeenSent flag unless we don't get an exception
					// will ensure that a resend attempt is made later
					log.warn("Could not deliver e-mail, inspection was not in proper state. Will re-try next time agent runs.", e);
				}
			}
		}
		session.flush();
		session.close();
		if (!inspections.isEmpty()) {
			log
			.debug("Found "
					+ inspections.size()
					+ " quick-booked inspections that had not received reminders, sent email to "
					+ count + " of them");	
		}		
	}

	private void sendInspectorEmail(Inspection inspection)
			throws EmailDeliveryException, InspectionProcessingException {
		Map<String, Object> props = new HashMap<String, Object>();
		props.put("inspection", inspection);
		props.put("inspectionLink", createInspectionLink(inspection));
		String toEmail = inspection.getInspector().getEmail();
		String subject = "[InspectorTime] Someone has scheduled a new inspection (needs attention)";
		AbstractMailMessage message = new StaticMailMessage(StaticMailMessageTemplateNames.INSPECTION_INCOMPLETE_FOR_INSPECTOR, props, toEmail, subject);
		this.mailService.send(message);
	}

	public void setSessionFactory(SessionFactory sessionFactory) {
		this.sessionFactory = sessionFactory;
	}

	public void setMailService(MailService mailService) {
		this.mailService = mailService;
	}
	
}
